#pragma once
#include <zGraphicsConfig.hpp>
#include <Math/Array.hpp>
#include <Utility/Tools.hpp>
#include <Utility/STLVector.hpp>
#include <Utility/HasFlag.hpp>
#include <Utility/StaticTools.hpp>
#include "../../Graphics/GeometryHelper.hpp"
#include "../ResourceManager.hpp"
#include "../Texture/Texture2D.hpp"
#include "../../Context/Context.hpp"
#include "../../VBO/VBO.hpp"
#include "../../VBO/ElementVBO.hpp"
#include "../../Graphics/AABB.hpp"
#include "../../Graphics/Rotation.hpp"
#include "../../Graphics/Translation.hpp"
#include "../../Graphics/Transformation.hpp"

namespace zzz{
const int MESH_POS      =0x00000001;
const int MESH_NOR      =0x00000002;
const int MESH_TEX      =0x00000004;
const int MESH_COLOR    =0x00000008;
const int MESH_ALL=MESH_POS|MESH_NOR|MESH_TEX|MESH_COLOR;

const int MESH_POSNOR    =0x00000100;
const int MESH_POSTAN    =0x00000200;
const int MESH_POSBINO    =0x00000400;
const int MESH_POSRATIO    =0x00000800;
const int MESH_POSALL=MESH_POSNOR|MESH_POSTAN|MESH_POSBINO|MESH_POSRATIO;

const int MESH_VBOPOS    =0x00010000;
const int MESH_VBONOR    =0x00020000;
const int MESH_VBOTEX    =0x00040000;
const int MESH_VBOCOLOR    =0x00080000;
const int MESH_VBOALL=MESH_VBOPOS|MESH_VBONOR|MESH_VBOTEX|MESH_VBOCOLOR;

const int MESH_EVBOPOS    =0x01000000;
const int MESH_EVBONOR    =0x02000000;
const int MESH_EVBOTEX    =0x04000000;
const int MESH_EVBOCOLOR  =0x08000000;
const int MESH_EVBOALL=MESH_EVBOPOS|MESH_EVBONOR|MESH_EVBOTEX|MESH_EVBOCOLOR;

//PN: Position Number: number of position in one face
//since VBO handles only float, to make it simpler, we think position type is always float
//since inside GPU, texture coordinate is always 3, we make it always Vector3f
template<zuint PN>
class ZGRAPHICS_CLASS Mesh : public HasFlags {
public:
  zzz::StaticAssert< PN>=3 > AT_LEAST_3_POINT_FACE;

  struct MeshGroup : public HasFlags {
    string name_;
    string matname_;

    //original data
    STLVector<Vector<PN,zint32> > facep_,facen_,facet_,facec_;

    //normal VBO
    VBO VBOPos_, VBONor_, VBOTex_, VBOColor_;

    //element data
    STLVector<Vector<PN,zuint32> > eindex_;
    ElementVBO VBOeIndex_;
};
  //original data
  STLVector<Vector3f32> pos_,nor_,tex_,color_;
  STLVector<MeshGroup*> groups_;
  //per vertex generalized data
  STLVector<Vector3f32> posnor_,postan_,posbino_;
  STLVector<zfloat32> posratio_;
  zfloat32 allarea_;

  //in element, data are shared, only index is group wise
  STLVector<Vector3f32> epos_,enor_,etex_,ecolor_;
  VBO VBOePos_,VBOeNor_,VBOeTex_,VBOeColor_;

  Vector3f32 MeshOffset_;
  AABB3f32 AABB_;

  //  bool loaded_,vbodone_,eledone_;

  Mesh():allarea_(0){}
  virtual ~Mesh(){Clear();}
  void SetDataFlags() {
    ClrFlag(MESH_ALL);
    if (!pos_.empty()) SetFlag(MESH_POS);
    if (!nor_.empty()) SetFlag(MESH_NOR);
    if (!tex_.empty()) SetFlag(MESH_TEX);
    if (!color_.empty()) SetFlag(MESH_COLOR);
    for (zuint i=0; i<groups_.size(); i++)
    {
      MeshGroup *g=groups_[i];
      g->ClrFlag(MESH_ALL);
      if (!g->facep_.empty()) g->SetFlag(MESH_POS);
      if (!g->facen_.empty()) g->SetFlag(MESH_NOR);
      if (!g->facet_.empty()) g->SetFlag(MESH_TEX);
      if (!g->facec_.empty()) g->SetFlag(MESH_COLOR);
    }
  }
  bool Clear() {
    nor_.clear();
    pos_.clear();
    tex_.clear();
    for (zuint i=0; i<groups_.size(); i++)
      delete groups_[i];
    groups_.clear();
    posnor_.clear();
    postan_.clear();
    posbino_.clear();
    posratio_.clear();
    AABB_.Reset();
    ClrAllFlags();
    return true;
  }

  bool Apply(const Translation<float> &offset) {
    if (HasNoFlag(MESH_POS)) return false;
    int len=pos_.size();
    for (int i=0; i<len; i++)
      pos_[i]=offset.Apply(pos_[i]);
    AABB_.SetOffset(offset);
    for (zuint i=0; i<groups_.size(); i++)
      groups_[i]->ClrFlag(MESH_VBOALL);
    ClrFlag(MESH_EVBOALL);
    return true;
  }

  bool Apply(const Rotation<float> &t) {
    if (HasNoFlag(MESH_POS)) return false;
    int len=pos_.size();
    for (int i=0; i<len; i++)
      pos_[i]=t.Apply(pos_[i]);
    len=nor_.size();
    for (int i=0; i<len; i++) {
      nor_[i]=t.Apply(nor_[i]);
      if (nor_[i].SafeNormalize()==0) ZLOG(ZVERBOSE)<<"Found a 0-length normal\n";
    }
    AABB_.Reset();
    AABB_+=pos_;
    for (zuint i=0; i<groups_.size(); i++)
      groups_[i]->ClrFlag(MESH_VBOALL);
    ClrFlag(MESH_EVBOALL);
    return true;
  }

  bool Apply(const Transformation<float> &t) {
    if (HasNoFlag(MESH_POS)) return false;
    int len=pos_.size();
    for (int i=0; i<len; i++)
      pos_[i]=t.Apply(pos_[i]);
    len=nor_.size();
    for (int i=0; i<len; i++) {
      nor_[i]=t.Apply(nor_[i]);
      nor_[i].Normalize();
    }
    AABB_.Reset();
    AABB_+=pos_;
    for (zuint i=0; i<groups_.size(); i++)
      groups_[i]->ClrFlag(MESH_VBOALL);
    ClrFlag(MESH_EVBOALL);
    return true;
  }

  bool Scale(float coef) {
    if (HasNoFlag(MESH_POS)) return false;
    for (zuint i=0; i<pos_.size(); i++)
      pos_[i]*=coef;
    AABB_.Min()*=coef;
    AABB_.Max()*=coef;
    for (zuint i=0; i<groups_.size(); i++)
      groups_[i]->ClrFlag(MESH_VBOALL);
    ClrFlag(MESH_EVBOALL);
    return true;
  }

  bool Scale(const Vector3f &coef) {
    if (HasNoFlag(MESH_POS)) return false;
    for (zuint i=0; i<pos_.size(); i++)
      pos_[i]*=coef;
    AABB_.Min()*=coef;
    AABB_.Max()*=coef;
    for (zuint i=0; i<groups_.size(); i++)
      groups_[i]->ClrFlag(MESH_VBOALL);
    ClrFlag(MESH_EVBOALL);
    return true;
  }

  bool ChangeCoordinate(const string &from, const string &to) {
    if (HasNoFlag(MESH_POS)) return false;
    if (from==to) { 
      return true;
    } else if ((from=="xy" && to=="yx") || (from=="yz" && to=="xy")) {
      //swap xy and inverse z
      for (zuint i=0; i<pos_.size(); i++) {
        Swap(pos_[i].x(),pos_[i].y());
        pos_[i].z()=-pos_[i].z();
      }
      for (zuint i=0; i<nor_.size(); i++) {
        Swap(nor_[i].x(),nor_[i].y());
        nor_[i].z()=-nor_[i].z();
      }
    } else if ((from=="xy" && to=="x-y") || (from=="x-y" && to=="xy")) {
      //inverse x and y
      for (zuint i=0; i<pos_.size(); i++) {
        pos_[i].y()=-pos_[i].y();
        pos_[i].z()=-pos_[i].z();
      }
      for (zuint i=0; i<nor_.size(); i++) {
        nor_[i].y()=-nor_[i].z();
        nor_[i].z()=-nor_[i].z();
      }
    } else if ((from=="xz" && to=="x-y") || (from=="x-y" && to=="xz") ||\
      (from=="xz" && to=="xy") || (from=="xy" && to=="xz")) {
        //inverse z and swap yz
        for (zuint i=0; i<pos_.size(); i++) {
          pos_[i].z()=-pos_[i].z();
          Swap(pos_[i].y(),pos_[i].z());
        }
        for (zuint i=0; i<nor_.size(); i++) {
          nor_[i].z()=-nor_[i].z();
          Swap(nor_[i].y(),nor_[i].z());
        }
    }
    //reset aabb
    AABB_.Reset();
    AABB_+=pos_;
    for (zuint i=0; i<groups_.size(); i++)
      groups_[i]->ClrFlag(MESH_VBOALL);
    ClrFlag(MESH_EVBOALL);
    return true;
  }

  bool MakeCenter() {
    return Apply(Translation<float>(-AABB_.Center()));
  }

  bool Normalize() {
    return Scale(1.0/AABB_.Diff().Max());
  }

  bool CalPosNormal(const bool area_weighted=false) {
    //calculate normal
    if (HasNoFlag(MESH_POS)) return false;

    //calculate posnor_
    const zuint posn=pos_.size();
    posnor_.assign(posn,Vector3f(0));
    SetFlag(MESH_POSNOR);
    posratio_.assign(posn,0);
    allarea_=0;
    SetFlag(MESH_POSRATIO);

    for (zuint idx=0;idx<groups_.size();idx++) {
      MeshGroup *g=groups_[idx];
      zuint facelen=g->facep_.size();
      STLVector<Vector<PN,int> > &facep=g->facep_, &facet=g->facet_;
      for (zuint i=0; i<facelen; i++) {
        //calculate area
        float thisarea;
        switch(PN) {
        case 3:
          thisarea=GeometryHelper::TriangleArea(pos_[facep[i][0]], pos_[facep[i][1]], pos_[facep[i][2]]);
          break;
        default:
          {
            STLVector<Vector3f> points;
            for (zuint j=0; j<PN; j++) points.push_back(pos_[facep[i][j]]);
            STLVector<Vector2f> points2;
            GeometryHelper::ProjectTo2D<3,float>(points,points2);
            thisarea=GeometryHelper::PolygonArea(points2);
          }
        }
        allarea_+=thisarea;
        posratio_[facep[i][0]]+=thisarea;
        posratio_[facep[i][1]]+=thisarea;
        posratio_[facep[i][2]]+=thisarea;
        //calculate face normal
        Vector3f v1=pos_[facep[i][1]]-pos_[facep[i][0]];
        Vector3f v2=pos_[facep[i][2]]-pos_[facep[i][0]];
        Vector3f facenormal=v1.Cross(v2);
        for (int j=0; j<3; j++)
          posnor_[facep[i][j]]+=area_weighted?facenormal*thisarea:facenormal;
      }
    }
    for (unsigned int i=0; i<posn; i++) {
      posnor_[i].SafeNormalize();
      posratio_[i]/=(allarea_*3.0);
    }

    //copy posnor to normal if normal does no exist
    if (HasNoFlag(MESH_NOR)) {
      nor_=posnor_;
      SetFlag(MESH_NOR);
      for (zuint idx=0;idx<groups_.size();idx++) {
        groups_[idx]->facen_=groups_[idx]->facep_;
        groups_[idx]->SetFlag(MESH_NOR);
      }
    }

    if (HasNoFlag(MESH_TEX)) return true;
    //calculate postan_ posbino_ from UV
    postan_.assign(posn,Vector3f(0));
    posbino_.assign(posn,Vector3f(0));
    SetFlag(MESH_POSTAN);
    SetFlag(MESH_POSBINO);

    for (zuint idx=0;idx<groups_.size();idx++) {
      MeshGroup *g=groups_[idx];
      if (g->HasNoFlag(MESH_TEX)) continue;
      zuint facelen=g->facep_.size();
      STLVector<Vector<PN,int> > &facep=g->facep_, &facet=g->facet_;
      for (zuint i=0; i<facelen; i++) {
        //calculate face normal
        Vector3f v1=pos_[facep[i][1]]-pos_[facep[i][0]];
        Vector3f v2=pos_[facep[i][2]]-pos_[facep[i][0]];
        //use tex coord to calculate face tangent and binormal
        Vector3f t1=tex_[facet[i][1]]-tex_[facet[i][0]];
        Vector3f t2=tex_[facet[i][1]]-tex_[facet[i][0]];
        Vector3f tan;
        tan[0]=v1[0]*t2[1]-v2[0]*t1[1];
        tan[1]=v1[1]*t2[1]-v2[1]*t1[1];
        tan[2]=v1[2]*t2[1]-v2[2]*t1[1];
        if (t1[0]*t2[1]-t2[0]*t1[1]<0.0f) tan=-tan;
        Vector3f bino;
        bino[0]=v1[0]*t2[0]-v2[0]*t1[0];
        bino[1]=v1[1]*t2[0]-v2[1]*t1[0];
        bino[2]=v1[2]*t2[0]-v2[2]*t1[0];
        if (t2[0]*t1[1]-t2[1]*t1[0]<0.0f) bino=-bino;
        for (int j=0; j<3; j++) {
          postan_[facep[i][j]]+=tan;
          posbino_[facep[i][j]]+=bino;
        }
      }
      g->SetFlag(MESH_POSTAN | MESH_POSBINO);
    }
    for (unsigned int i=0; i<posn; i++) {
      postan_[i].SafeNormalize();
      posbino_[i].SafeNormalize();
    }

    return true;
  }

  //simple draw and vbo draw
  bool Draw(int bt=MESH_ALL) const {
    if (HasNoFlag(MESH_POS)) return false;
    for (zuint i=0; i<groups_.size(); i++) {
      if (!Draw(i,bt)) 
        return false;
    }
    return true;
  }

  bool Draw(int idx, int bt) const {
    if (HasNoFlag(MESH_POS)) return false;
    MeshGroup *g=groups_[idx];
    bool donor=CheckBit(bt,MESH_NOR) && g->HasFlag(MESH_NOR);
    bool dotex=CheckBit(bt,MESH_TEX) && g->HasFlag(MESH_TEX);
    bool docolor=CheckBit(bt,MESH_COLOR) && g->HasFlag(MESH_COLOR);
    STLVector<Vector<PN,int> > &facep=g->facep_, &facen=g->facen_, &facet=g->facet_, &facec=g->facec_;

    switch(PN) {
    case 0:
    case 1:
    case 2:
      return false;
    case 3: glBegin(GL_TRIANGLES); break;
    case 4: glBegin(GL_QUADS); break;
    default: glBegin(GL_POLYGON); break;
    }
    for (zuint i=0; i<facep.size(); i++) {
      for (int j=0; j<PN; j++) {
        if (donor) glNormal3fv(nor_[facen[i][j]].Data());
        if (dotex) glTexCoord3fv(tex_[facet[i][j]].Data());
        if (docolor) glColor3fv(color_[facec[i][j]].Data());
        glVertex3fv(pos_[facep[i][j]].Data());
      }
    }

    glEnd();
    return true;
  }
  bool Draw(const string &name, int bt) const {
    if (HasNoFlag(MESH_POS)) return false;
    for (zuint i=0; i<groups_.size(); i++)
      if (groups_[i]->name_==name) return Draw(i,bt);
    return false;
  }
  bool CreateVBO(int bt=MESH_ALL) {
    if (HasNoFlag(MESH_POS)) return false;
    if (HasNoFlag(MESH_NOR)) ClrFlag(MESH_NOR);
    if (HasNoFlag(MESH_TEX)) ClrFlag(MESH_TEX);
    if (HasNoFlag(MESH_COLOR)) ClrFlag(MESH_COLOR);

    for (zuint i=0; i<groups_.size(); i++)
      if (!CreateVBO(i,bt)) return false;
    return true;
  }
  bool DrawVBO(int bt=MESH_ALL) {
    for (zuint i=0; i<groups_.size(); i++)
      if (!DrawVBO(i,bt)) return false;
    return true;
  }

  bool DrawVBO(int idx, int bt) {
    MeshGroup *g=groups_[idx];
    if (g->HasNoFlag(MESH_VBOPOS)) {
      if (!CreateVBO(idx, bt)) return false;
    }
//     if (g->HasNoFlag(MESH_VBONOR)) g->ClrFlag(MESH_NOR);
//     if (g->HasNoFlag(MESH_VBOTEX)) g->ClrFlag(MESH_TEX);
//     if (g->HasNoFlag(MESH_VBOCOLOR)) g->ClrFlag(MESH_COLOR);
    if (BindVBO(idx, bt)==false) return false;
    switch(PN) {
    case 0:
    case 1:
    case 2:
    default:
      return false;
    case 3: glDrawArrays(GL_TRIANGLES,0,(int)groups_[idx]->facep_.size()*3); break;
    case 4: glDrawArrays(GL_QUADS,0,(int)groups_[idx]->facep_.size()*4); break;
    }
    UnbindVBO(idx);
    return true;
  }

  bool DrawVBO(const string &name, int bt) {
    for (zuint i=0; i<groups_.size(); i++)
      if (groups_[i]->name_==name) return DrawVBO(i,bt);
    ZLOG(ZERROR)<<"Cannot find group: "<<name<<endl;
    return false;
  }

  bool CreateElement(int bt=MESH_ALL) {
    if (HasNoFlag(MESH_POS)) return false;
    ClrFlag(MESH_EVBOALL);
    epos_.clear();
    enor_.clear();
    etex_.clear();
    ecolor_.clear();

    bool donor=CheckBit(bt,MESH_NOR) && HasFlag(MESH_NOR);
    bool dotex=CheckBit(bt,MESH_TEX) && HasFlag(MESH_TEX);
    bool docolor=CheckBit(bt,MESH_COLOR) && HasFlag(MESH_COLOR);

    typedef Vector<4,zuint> elementpoint;
    map<elementpoint,zuint> elementpointmap;

    for (zuint idx=0;idx<groups_.size();idx++) {
      MeshGroup *g=groups_[idx];
      bool thisdonor=donor && g->HasFlag(MESH_NOR);
      bool thisdotex=dotex && g->HasFlag(MESH_TEX);
      bool thisdocolor=docolor && g->HasFlag(MESH_COLOR);
      STLVector<Vector<PN,int> > &facep=g->facep_, &facen=g->facen_, &facet=g->facet_, &facec=g->facec_;
      g->eindex_.clear();
      int facesize=facep.size();
      for (int i=0; i<facesize; i++) {
        Vector<PN,zuint> index;
        for (int j=0; j<PN; j++) {
          elementpoint p(0);
          p[0]=facep[i][j];
          if (thisdonor) p[1]=facen[i][j];
          if (thisdotex) p[2]=facet[i][j];
          if (thisdocolor) p[3]=facec[i][j];
          map<elementpoint,zuint>::const_iterator mi=elementpointmap.find(p);
          if (mi==elementpointmap.end()) {
            epos_.push_back(pos_[facep[i][j]]);
            if (thisdonor) enor_.push_back(nor_[facen[i][j]]);
            else if (donor) enor_.push_back(Vector3f(0));
            if (thisdotex) etex_.push_back(tex_[facet[i][j]]);
            else if (dotex) etex_.push_back(Vector3f(0));
            if (thisdocolor) ecolor_.push_back(color_[facec[i][j]]);
            else if (docolor) ecolor_.push_back(Vector3f(0));
            index[j]=epos_.size()-1;
            elementpointmap[p]=index[j];
          } else {
            index[j]=mi->second;
          }
        }
        g->eindex_.push_back(index);
      }
      if (GLExists())
        g->VBOeIndex_.Create(g->eindex_[0].Data(),(int)g->eindex_.size(),VBODescript::Element3ui);
    }  

    //Create Element VBO
    ClrFlag(MESH_EVBOALL);
    if (GLExists()) {
      SetFlag(MESH_EVBOPOS);
      VBOePos_.Create(epos_[0].Data(),(int)epos_.size(),VBODescript::Vertex3f);
      if (donor) {
        SetFlag(MESH_EVBONOR);
        VBOeNor_.Create(enor_[0].Data(),(int)enor_.size(),VBODescript::Normalf);
      }
      if (dotex) {
        SetFlag(MESH_EVBOTEX);
        VBOeTex_.Create(etex_[0].Data(),(int)etex_.size(),VBODescript::TexCoord3f);
      }
      if (docolor) {
        SetFlag(MESH_EVBOCOLOR);
        VBOeColor_.Create(ecolor_[0].Data(),(int)ecolor_.size(),VBODescript::Color3f);
      }
    }
    return true;
  }

  bool DrawElement(int bt=MESH_ALL) {
    if (HasNoFlag(MESH_EVBOPOS)) {
      if (!CreateElement(bt))
        return false;
    }
    for (zuint i=0; i<groups_.size(); i++)
      if (!DrawElement(i,bt)) return false;
    return true;
  }

  bool DrawElement(int idx, int bt) {
    if (HasNoFlag(MESH_EVBOPOS)) return false;
    if (HasNoFlag(MESH_EVBONOR)) ClrFlag(MESH_NOR);
    if (HasNoFlag(MESH_EVBOTEX)) ClrFlag(MESH_TEX);
    if (HasNoFlag(MESH_EVBOCOLOR)) ClrFlag(MESH_COLOR);
    if (BindElement(idx,bt)==false) return false;
    switch(PN) {
    case 0:
    case 1:
    case 2:
    default: return false;
    case 3: glDrawElements(GL_TRIANGLES,(int)groups_[idx]->eindex_.size()*3,GL_UNSIGNED_INT,0); break;
    case 4: glDrawElements(GL_QUADS,(int)groups_[idx]->eindex_.size()*4,GL_UNSIGNED_INT,0); break;
    }
    UnbindElement(idx);
    return true;
  }

  bool DrawElement(const string &name, int bt) {
    if (HasNoFlag(MESH_EVBOPOS)) return false;
    for (zuint i=0; i<groups_.size(); i++)
      if (groups_[i]->name_==name) return DrawElement(i,bt);
    return false;
  }

private:
  //normal VBO manipulation
  bool CreateVBO(int idx, int bt) {
    MeshGroup *g=groups_[idx];
    bool donor=CheckBit(bt,MESH_NOR) && g->HasFlag(MESH_NOR);
    bool dotex=CheckBit(bt,MESH_TEX) && g->HasFlag(MESH_TEX);
    bool docolor=CheckBit(bt,MESH_COLOR) && g->HasFlag(MESH_COLOR);
    STLVector<Vector<PN,int> > &facep=g->facep_, &facen=g->facen_, &facet=g->facet_, &facec=g->facec_;

    int nVert=(int)facep.size()*PN;
    Vector3f *buffer=new Vector3f[nVert];

    {
      int size=facep.size(), cur=0;
      for (int j=0; j<size; j++) for (int k=0; k<PN; k++)
        buffer[cur++]=pos_[facep[j][k]];
      g->VBOPos_.Create(buffer,nVert,VBODescript::Vertex3f);
      g->SetFlag(MESH_VBOPOS);
    }

    if (donor) {
      int size=facen.size(), cur=0;
      for (int j=0; j<size; j++) for (int k=0; k<PN; k++)
        buffer[cur++]=nor_[facen[j][k]];
      g->VBONor_.Create(buffer,nVert,VBODescript::Normalf);
      g->SetFlag(MESH_VBONOR);
    }

    if (dotex) {
      int size=facet.size(), cur=0;
      for (int j=0; j<size; j++) for (int k=0; k<PN; k++)
        buffer[cur++]=tex_[facet[j][k]];
      g->VBOTex_.Create(buffer,nVert,VBODescript::TexCoord3f);
      g->SetFlag(MESH_VBOTEX);
    }

    if (docolor) {
      int size=facep.size(), cur=0;
      for (int j=0; j<size; j++) for (int k=0; k<PN; k++)
        buffer[cur++]=color_[facec[j][k]];
      g->VBOColor_.Create(buffer,nVert,VBODescript::Color3f);
      g->SetFlag(MESH_VBOCOLOR);
    }

    delete[] buffer;
    return true;
  }

  bool BindVBO(int idx, int bt) {
    // It's not MESH_VBOPOS, since BT always accept MESH_POS
    if (CheckBit(bt, MESH_POS)) groups_[idx]->VBOPos_.Bind();
    if (CheckBit(bt, MESH_NOR)) groups_[idx]->VBONor_.Bind();
    if (CheckBit(bt, MESH_TEX)) groups_[idx]->VBOTex_.Bind();
    if (CheckBit(bt, MESH_COLOR)) groups_[idx]->VBOColor_.Bind();
    return true;
  }
  bool UnbindVBO(int idx) {
    groups_[idx]->VBOPos_.Unbind();
    groups_[idx]->VBONor_.Unbind();
    groups_[idx]->VBOTex_.Unbind();
    groups_[idx]->VBOColor_.Unbind();
    return true;
  }
  //element manipulation
  bool BindElement(int idx, int bt) {
    // It's not MESH_VBOPOS, since BT always accept MESH_POS
    if (CheckBit(bt, MESH_POS)) VBOePos_.Bind();
    if (CheckBit(bt, MESH_NOR)) VBOeNor_.Bind();
    if (CheckBit(bt, MESH_TEX)) VBOeTex_.Bind();
    if (CheckBit(bt, MESH_COLOR)) VBOeColor_.Bind();
    groups_[idx]->VBOeIndex_.Bind();
    return true;
  }
  bool UnbindElement(int idx) {
    VBOePos_.Unbind();
    VBOeNor_.Unbind();
    VBOeTex_.Unbind();
    VBOeColor_.Unbind();
    groups_[idx]->VBOeIndex_.Unbind();
    return true;
  }

};

typedef Mesh<3> TriMesh;
typedef Mesh<4> QuadMesh;
}
